﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="DataCenterMapper.cs" company="">
//   
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace ExecutionTool.ImportExport
{
    using System;
    using System.Collections.Generic;
    using System.Configuration;
    using System.Net;
    using System.Text.RegularExpressions;
    using System.Threading.Tasks;

    #region DataCenterMapper

    /// <summary>
    /// NOTE: This is NOT an approved way to calculate where a server is located by datacenter
    /// This is brittle to changes in the DNS servers per Microsoft datacenter.
    /// </summary>
    public class DataCenterMapper
    {
        #region Constants

        internal const string Pattern = @"data\.([A-Za-z0-9\-]+)\.database\.windows\.net";

        #endregion

        #region Static Fields

        internal static readonly Regex Regex = new Regex(Pattern, RegexOptions.Compiled);

        private static readonly Dictionary<string, DataCenter> DatacenterMap =
            new Dictionary<string, DataCenter>(StringComparer.InvariantCultureIgnoreCase);

        #endregion

        #region Constructors and Destructors

        static DataCenterMapper()
        {
            DataCenterMappingSection section = (DataCenterMappingSection)ConfigurationManager.GetSection("DataCenterMapping");

            if (section == null)
            {
                throw new ArgumentException("Cannot load DataCenterMapping.  Ensure the .config file is in same directory with the executable.");
            }

            DataCenterCollection dccollection = section.DataCenterItems;

            foreach (DataCenter dc in dccollection)
            {
                DatacenterMap.Add(dc.DnsPrefix, dc);
            }
        }

        #endregion

        #region Public Methods and Operators

        public static DataCenter GetDatacenterFromServerName(string serverName)
        {
            IPHostEntry host = GetHostEntry(serverName);
            string alias = host.HostName;

            if (!host.HostName.EndsWith("INT.MSCDS.COM", StringComparison.InvariantCultureIgnoreCase))
            {
                Match match = Regex.Match(host.HostName);
                if (!match.Success)
                {
                    throw new ArgumentException(string.Format("Unable to detect data center for the server name {0}", serverName));
                }

                alias = match.Groups[1].Value;
            }

            DataCenter dc;
            if (!DatacenterMap.TryGetValue(alias.ToUpperInvariant(), out dc))
            {
                throw new ArgumentException(string.Format("Unable to detect data center for the server name {0}", serverName));
            }

            return dc;
        }

        public static Task<DataCenter> GetDatacenterFromServerNameAsync(string serverName)
        {
            var task = Task<IPHostEntry>.Factory.FromAsync(Dns.BeginGetHostEntry, Dns.EndGetHostEntry, serverName, null).ContinueWith(
                t =>
                    {
                        var errorMessage = string.Format("Unable to detect data center for the server name {0}", serverName);

                        if (t.IsFaulted)
                        {
                            throw new ArgumentException(errorMessage, "serverName", t.Exception);
                        }

                        var host = t.Result;
                        var alias = host.HostName;

                        if (!host.HostName.EndsWith("INT.MSCDS.COM", StringComparison.InvariantCultureIgnoreCase))
                        {
                            var match = Regex.Match(host.HostName);
                            if (!match.Success)
                            {
                                throw new ArgumentException(errorMessage, serverName);
                            }

                            alias = match.Groups[1].Value;
                        }

                        DataCenter dc;
                        if (!DatacenterMap.TryGetValue(alias.ToUpperInvariant(), out dc))
                        {
                            throw new ArgumentException(errorMessage, "serverName");
                        }

                        return dc;
                    });

            return task;
        }

        #endregion

        #region Methods

        private static IPHostEntry GetHostEntry(string serverName)
        {
            IPHostEntry host;
            try
            {
                host = Dns.GetHostEntry(serverName);
            }
            catch (Exception ex)
            {
                throw new ArgumentException(string.Format("Unable to detect data center for the server name {0}", serverName), ex);
            }

            return host;
        }

        #endregion
    }

    #endregion
}
